#pragma once
#include <string>
#include <vector>
#include <unordered_map>
#include <iostream>
#include <sstream>
#include <memory>
#include <fstream>
#include <functional>
static const std::string REQ_LINE_SEP = "\r\n";   // 请求的每一行以"\r\n"分隔
static const std::string REQ_HEAD_SEP = ": ";     // 请求报头kv以": "分隔
static const std::string WWWROOT = "wwwroot";     // wwwroot目录下存放服务器提供的所有资源，在客户端上访问的根目录实际上是wwwroot目录，也叫做web根目录
static const std::string HOMEPAGE = "index.html"; // web根目录下的index.html文件是客户端访问根目录时，http协议提供的默认页面。
static const std::string VERSION = "HTTP/1.1";    // 服务器响应的HTTP版本
static const std::string RESP_LINE_SEP = "\r\n";
static const std::string RESP_HEAD_SEP = ": ";
static const std::string SPACE = " ";
static const std::string FILE_SUFFIX_SEP = ".";
static const std::string ARGS_SEP = "?"; // 参数的分隔符 /login?username=xxx&password=xxx

class HttpResponse;
class HttpRequest;
using func_t = std::function<std::shared_ptr<HttpResponse>(std::shared_ptr<HttpRequest>)>; // 参数request 返回值response
/*关于http协议：
1.http协议本质是一个由浏览器帮用户从目标服务器获取资源的应用程序，程序的客户端是浏览器，服务端是各个平台。
2.当我们访问一个网页时，浏览器会向对应的网页发送http请求，得到的资源是在服务器中的某个特定目录下的。
3.访问网页的本质是访问服务器某个路径下的资源，这个请求是浏览器帮助我们发送的。
4.显示一个网页可能会显示多个音视频资源，访问网页时浏览器同时也会申请访问这些音视频资源。因此访问一个网页浏览器可能会向服务器发送多个请求*/
/*
HTTP服务客户端是浏览器，对Request进行序列化由浏览器完成，服务器对Request只需要做反序列化
HttpRequest:
请求行
请求报头
空行
请求正文
服务器只需要对Response进行序列化，由浏览器对Response进行反序列化
HttpResponse:
状态行
响应报头
空行
相应正文
*/

/*HTTP请求的方法:
GET:用于从服务器上获取数据，同时可以向服务器以url的方式提交参数
POST:用于向服务器以content的方式提交参数
POST比GET更私密，且能提交的参数更多
理解浏览器提交关键词
*/
class HttpRequest
{
public:
    HttpRequest()
        : _empty_line(REQ_LINE_SEP), _path(WWWROOT)
    {
    }
    ~HttpRequest() {}

    void Serialize(std::string *str)
    {
    }
    void Deserialize(std::string &req_str)
    {
        // 提取请求行
        _req_line = GetOneLine(req_str);
        // 提取请求头
        std::string line;
        while ((line = GetOneLine(req_str)) != REQ_LINE_SEP)
        {
            _req_headers.emplace_back(line);
        }
        // 提取请求正文
        _req_content = req_str;
        // 反序列化我们希望看到的成员
        ParseReqLine();
        ParseHeaders();
    }
    std::string GetOneLine(std::string &req_str)
    {
        auto pos = req_str.find(REQ_LINE_SEP);
        // pos==0说明req_str是empty_line，直接返回行分隔符
        if (pos == 0)
        {
            req_str.erase(0, REQ_LINE_SEP.size());
            return REQ_LINE_SEP;
        }
        // 没有找到行分隔符，说明将req_str分隔完了，直接返回空串
        if (pos == std::string::npos)
            return std::string();
        // 此时一定能够提取行
        std::string line = req_str.substr(0, pos);
        // 每次提取完后都将提取的行删除
        req_str.erase(0, pos + REQ_LINE_SEP.size());
        return line;
    }
    bool ParseArgs()
    {
        // 只有请求方法为get时，才需要提取
        if (strcasecmp(_method.c_str(), "GET") == 0)
        {
            LOG(DEBUG, "change url begin, url: %s", _url.c_str());
            size_t pos = _url.find(ARGS_SEP);
            if (pos != std::string::npos)
            {
                _args = _url.substr(pos); // url:/login?user=xxx
                _url.resize(pos);         // url:/login
                LOG(DEBUG, "change url done, url: %s", _url.c_str());
                return true;
            }
            else return false;
        }
        return false;
    }
    bool ParseReqLine()
    {
        if (_req_line.empty())
            return false;
        std::stringstream ss(_req_line);
        ss >> _method >> _url >> _version;
        // 提取url中的参数
        ParseArgs();

        _path += _url; // path:wwwroot/login
        // 是否访问默认首页
        if (_path.back() == '/')
            _path += HOMEPAGE;

        auto pos = _path.rfind(FILE_SUFFIX_SEP);
        if (pos == std::string::npos)
        {
            _suffix = ".html";
        }
        else
        {
            _suffix = _path.substr(pos);
        }
        return true;
    }
    void ParseHeaders()
    {
        for (auto &header : _req_headers)
        {
            std::string k, v;
            ParseHeadersHelper(header, &k, &v);
            _req_headers_map.insert(std::make_pair(k, v));
        }
    }

    void Print()
    {
        // 原来的request格式
        std::cout << "#############" << std::endl;
        std::cout << _req_line << std::endl;
        for (auto &line : _req_headers)
            std::cout << line << std::endl;
        std::cout << _empty_line;
        std::cout << _req_content;

        // 修改后的request格式
        std::cout << "@@@@@@@@@@@@@" << std::endl;
        std::cout << "method ### " << _method << std::endl;
        std::cout << "URL ### " << _url << std::endl;
        std::cout << "PATH ### " << _path << std::endl;
        std::cout << "HTTP VERSION ### " << _version << std::endl;

        for (auto &header : _req_headers_map)
        {
            std::cout << "@@@" << header.first << ": " << header.second << std::endl;
        }
    }

    bool IsExcute()
    {
        return !_args.empty() || !_req_content.empty(); // 参数不为空或者内容不为空，说明客户端提交过参数，需要进行处理
    }
    std::string Path()
    {
        return _path;
    }
    std::string Suffix()
    {
        return _suffix;
    }
    std::string Method()
    {
        return _method;
    }
    std::string Text()
    {
        return _req_content;
    }
    std::string Args()
    {
        return _args;
    }

private:
    void ParseHeadersHelper(const std::string &str, std::string *k, std::string *v)
    {
        auto pos = str.find(REQ_HEAD_SEP);
        *k = str.substr(0, pos);
        *v = str.substr(pos + REQ_HEAD_SEP.size());
    }
    // 这是http请求的标准格式
    std::string _req_line;
    std::vector<std::string> _req_headers;
    std::string _empty_line;
    std::string _req_content = std::string();
    // 这是我们期望看到的格式
    std::string _method;               // 请求的方法 get / post
    std::string _args = std::string(); // 客户端提交的参数
    std::string _url;                  // 客户端请求资源时的路径 /xxx
    std::string _path;                 // 资源在服务器上的路径 wwwroot/xxx，当用户没有指定获取具体文件对应的资源时，显示默认首页index.html
    std::string _version;              // http版本
    std::string _suffix;               // 请求资源类型的后缀
    // 键值对的方式存放请求报头
    std::unordered_map<std::string, std::string> _req_headers_map;
};

class HttpResponse
{
public:
    HttpResponse()
        : _empty_line(std::string())
    {
    }
    ~HttpResponse()
    {
    }
    void AddStatusLine(int code, const std::string &desc)
    {
        _version = VERSION;
        _code = code;
        _desc = desc;
    }
    void AddHeadersMap(const std::string &k, const std::string &v)
    {
        _headers_map[k] = v;
    }
    void AddContent(const std::string &content)
    {
        _resp_content = content;
    }
    std::string Serialize()
    {
        // 构建响应状态行
        _status_line = _version + SPACE + std::to_string(_code) + SPACE + _desc + RESP_LINE_SEP;
        // 构建响应头
        for (auto &header : _headers_map)
        {
            std::string head = header.first + RESP_HEAD_SEP + header.second + RESP_LINE_SEP;
            _headers.push_back(head);
        }
        // 构建序列化字符串
        std::string resp_str = _status_line;
        for (auto &header : _headers)
        {
            resp_str += header;
        }
        resp_str += (_empty_line + RESP_LINE_SEP);
        resp_str += _resp_content;

        return resp_str;
    }
    void Print()
    {
        std::cout << std::endl;
        std::cout << _status_line;
        for (auto &string : _headers)
        {
            std::cout << string;
        }
        std::cout << _empty_line;
        std::cout << REQ_LINE_SEP;
        std::cout << _resp_content;
    }

private:
    // 期望看到的格式
    std::string _version; // HTTP版本
    int _code;            // 状态码
    std::string _desc;    // 状态码描述
    std::unordered_map<std::string, std::string> _headers_map;

    // 结构化的请求格式
    std::string _status_line;
    std::vector<std::string> _headers;
    std::string _empty_line;
    std::string _resp_content;
};
class Factory
{
public:
    static std::shared_ptr<HttpRequest> BulidHttpRequest()
    {
        return std::make_shared<HttpRequest>();
    }
    static std::shared_ptr<HttpResponse> BuildHttpResponse()
    {
        return std::make_shared<HttpResponse>();
    }
};
class HttpServer
{
public:
    HttpServer()
    {
        // 初始化mime
        _mime_type.insert(std::make_pair(".html", "text/html"));
        _mime_type.insert(std::make_pair(".css", "text/css"));
        _mime_type.insert(std::make_pair(".js", "application/x-javascript"));
        _mime_type.insert(std::make_pair(".jpg", "image/jpeg"));
        _mime_type.insert(std::make_pair(".png", "image/png"));
        _mime_type.insert(std::make_pair(".ico", "application/x-ico"));

        _code_to_desc.insert(std::make_pair(200, "ok"));
        _code_to_desc.insert(std::make_pair(301, "MovedPermanently"));
        _code_to_desc.insert(std::make_pair(302, "SeeOther"));
        _code_to_desc.insert(std::make_pair(404, "NotFound"));
        _code_to_desc.insert(std::make_pair(500, "Internel Server Error"));
    }
    ~HttpServer() {}
    std::string ReadFileContent(const std::string &path, int *file_size)
    {
        // 二进制读文件
        std::ifstream ifs(path.c_str(), std::ios::binary);
        if (!ifs.is_open())
        {
            return std::string();
        }
        // 获取文件大小
        ifs.seekg(0, ifs.end);
        *file_size = ifs.tellg();
        ifs.seekg(0, ifs.beg);
        // std::cout << *file_size;
        std::string content;
        content.resize(*file_size); // 注意这里要使用resize而不是reserve(我们需要自己扩大size)
        ifs.read((char *)content.c_str(), *file_size);

        ifs.close();
        return content;
    }
    void AddServer(std::string serve, func_t func)
    {
        _func[serve] = func;
    }

    // #define TEST
    std::string HttpServerHelper(std::string req_str)
#ifdef TEST
    {
        std::cout << req_str;
        return std::string();
    }
#else
    {
        std::cout << "-------------------------------------------------\n";
        //std::cout << req_str;
        auto req = Factory::BulidHttpRequest();
        // 反序列化
        req->Deserialize(req_str);

        // 客户端请求的是一个服务, 服务器对提交的参数进行处理
        if (req->IsExcute())
        {
            std::string serve = req->Path().substr(WWWROOT.size()); // serve:/login path:wwwroot/login
            auto response = _func[serve](req);                      // 将请求交给对应的处理函数
            return response->Serialize();
        }
        // 客户端请求的是静态网页，构建应答并返回
        else
        // 1. 客户端请求一个需要重定向的链接
        {
            // 填充应答
            auto resp = Factory::BuildHttpResponse();
            int code;
            std::string newurl = "http://110.41.66.154:8888/1.html";

            if (req->Path() == "wwwroot/redir")
            {
                code = 301;
                resp->AddStatusLine(code, _code_to_desc[code]);
                resp->AddHeadersMap("Location", newurl);
                LOG(INFO, "client get a link to redir: %s", newurl.c_str());
            }
            else
            {
                // 1. 填充content-type
                std::string suffix = req->Suffix();
                std::string content_type = _mime_type[suffix];
                resp->AddHeadersMap("Content-Type", content_type);

                // 2. 填充content
                std::string content_path = req->Path();
                int file_size = 0;
                std::string resp_content = ReadFileContent(content_path, &file_size);
                // 2.1 判断访问的页面是否存在
                if (resp_content.empty())
                {
                    code = 404;
                    // 跳转到404页面
                    resp_content = ReadFileContent("wwwroot/404.html", &file_size);
                    resp->AddContent(resp_content);
                    LOG(INFO, "client get a link to 404 page, path: %s", content_path.c_str());
                }
                else
                {
                    code = 200;
                    resp->AddContent(resp_content);
                    LOG(INFO, "client get a link to %s, content-length:%d, content-type:%s", content_path.c_str(), file_size, content_type.c_str());
                }

                // 3.填充内容大小
                resp->AddHeadersMap("Content-Length", std::to_string(file_size));
            }
            // 填充状态行
            resp->AddStatusLine(code, _code_to_desc[code]);
            // 发送应答
            return resp->Serialize();
        }
    }
#endif
private:
    std::unordered_map<std::string, std::string> _mime_type; // 不同文件类型对应的content-type
    std::unordered_map<int, std::string> _code_to_desc;      // 状态码对应的描述
    std::unordered_map<std::string, func_t> _func;           // 用于对提交上来的参数进行处理
};